// Package auth
/**
* @Project : geektime-basic-go-study
* @File    : service.go
* @IDE     : GoLand
* @Author  : Tvux
* @Date    : 2024/10/23 15:25
**/

package auth

import (
	"context"
	"errors"
	"fmt"
	"geektime-basic-go-study/webook/backend/internal/service/sms"
	"github.com/golang-jwt/jwt/v5"
)

type SMSAuthService struct {
	smsSvc sms.Service
	key    []byte
}

func (Self *SMSAuthService) Send(ctx context.Context, authToken string, args []string, phones ...string) error {
	auth, err := Self.parseJWT(authToken)
	if err != nil {
		return fmt.Errorf("failed to parse JWT: %w", err)
	}

	return Self.smsSvc.Send(ctx, auth.TplId, args, phones...)
}

func (Self *SMSAuthService) GetProvider() sms.ProviderName {
	return Self.smsSvc.GetProvider()
}

func NewSMSAuthService(smsSvc sms.Service, key []byte) *SMSAuthService {
	return &SMSAuthService{
		smsSvc: smsSvc,
		key:    key,
	}
}

type SMSAuthClaims struct {
	jwt.RegisteredClaims
	TplId string
}

func (Self *SMSAuthService) parseJWT(authToken string) (*SMSAuthClaims, error) {
	token, err := jwt.ParseWithClaims(authToken, &SMSAuthClaims{}, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return Self.key, nil
	})

	if err != nil {
		return nil, err
	}

	if !token.Valid {
		return nil, errors.New("invalid token")
	}

	if claims, ok := token.Claims.(*SMSAuthClaims); ok {
		return claims, nil
	} else {
		return nil, errors.New("failed to parse SMSAuthClaims")
	}
}
